home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1994 / MacHack 1994.toast / MacHack™ 1987-1994 / MacHack™ '93 / Hacks '93 / Mystery Science Mac / App Sources / BackTalk.c next >
Encoding:
C/C++ Source or Header  |  1993-09-01  |  14.7 KB  |  590 lines  |  [TEXT/KAHL]

  1. /*
  2.     File:        BackTalk.c
  3.  
  4.     Contains:    Source to BackTalk!, part of MacHack '93 entry "MSM 3k"
  5.  
  6.     Written by:    Bill Britton, John Arkley, and Jorg Brown
  7.  
  8. */
  9.  
  10. #undef SystemSevenOrLater
  11. #define SystemSevenOrLater 1
  12.  
  13. #include <diskinit.h>
  14. #include <errors.h>
  15. #include <resources.h>
  16. #include <processes.h>
  17. #include <fixmath.h>
  18. #include <GestaltEqu.h>
  19. #include "Speech.h"
  20. #include <Sound.h>
  21.  
  22. typedef unsigned char  uchar;
  23. typedef unsigned short ushort;
  24. typedef unsigned long  ulong;
  25.  
  26. #define    kOSEvent                app4Evt    /* event used by MultiFinder */
  27. #define    kSuspendResumeMessage    1        /* high byte of suspend/resume event message */
  28. #define    kResumeMask                1        /* bit of message field for resume vs. suspend */
  29. #define    kMouseMovedMessage        0xFA    /* high byte of mouse-moved event message */
  30. #define    kNoEvents                0        /* no events mask */
  31.  
  32. #define rMenuBar    128
  33.  
  34. #define mApple        128
  35. #define iAbout        1
  36.  
  37. #define mFile        129
  38. #define iNoCmnt        1
  39. #define iQuit        3
  40.  
  41. #define mVoice        130
  42. #define iPrefs        1
  43.  
  44. #define kMaxVoices    40
  45.  
  46. Boolean        gInBackground;        /* maintained by Initialize and DoEvent */
  47.  
  48. /* gSpeechChan is a global speech channel which can be used for speaking */
  49. /* gSpeechStr is a global pascal string which can be used for speech */
  50. /* gSpeechErr is a global error return which can be latched if desired */
  51.  
  52. SpeechChannel    gSpeechChan = NULL;
  53. Str255            gSpeechStr = "\pMST3000";
  54. uchar            gTextBuf [4096] = {0};
  55. ushort            gTextBufLen = 0;
  56. OSErr            gSpeechErr = noErr;
  57.  
  58. VoiceSpec        gVspec[kMaxVoices];
  59. short            gVoiceSel;
  60. short            gVoiceCount = 0;
  61. Fixed            gSpeechRate = 0;
  62.  
  63. ProcessSerialNumber    gOurPSN;
  64.  
  65. Boolean            gNoComment = false;    /* it is possible to have the app not talk via menu selection */
  66.                                     /* this was done because we wanted to be running during other hacks */
  67.                                     /* and we might get too obnoxious for some */
  68.  
  69. ulong            gLastSpeak = 0;
  70.  
  71. extern void _DataInit( void );
  72. extern pascal OSErr SM_InitializeSpeechManager( void ); /* SPEECH MANAGER: INIT startup -- allocates globals, installs dispatcher */
  73.  
  74. void DoPrefs( void );
  75. void Initialize( void );
  76. void DoIdle( void );
  77. void EventLoop( void );
  78. void DoActivate( WindowPtr fWind, Boolean becomingActive );
  79. void DoMenuCommand( long menuResult );
  80. void SaySomething( char *sayThis, Boolean regardless );
  81. void DoUpdate( WindowPtr upWind );
  82. void DoAbout( void );
  83. void StartupSpeech( void );
  84. void Terminate( void );
  85. void DoEvent( EventRecord *event );
  86. void DrawMST3( void );
  87. void DoNoComment( void );
  88. void AdjustMenus( void );
  89. void MST3_ChangeRate (Fixed newRate);
  90. void MST3_ChangePitch (long newPitch);
  91. void MST3_ChangeVoiceSel (long newVoice);
  92.  
  93.  
  94.  
  95. /* Define HiWrd and LoWrd macros for efficiency. */
  96. #define HiWrd(aLong)    (((aLong) >> 16) & 0xFFFF)
  97. #define LoWrd(aLong)    ((aLong) & 0xFFFF)
  98.  
  99.  
  100. typedef struct KeyMapp {
  101.     short    wasted;
  102.     short wasted2;
  103.     short    wasted3;
  104.     short    command : 1;
  105.     short : 5;
  106.     short space : 1;
  107.     short    tab : 1;
  108.     short : 4;
  109.     short control : 1;
  110.     short option : 1;
  111.     short capslock : 1;
  112.     short shift : 1;
  113. } KeyMapp;
  114.  
  115. #define KM (*(KeyMapp *)0x174)
  116.  
  117. void DrawMST3( void )
  118. {
  119.     /* we used this to convert the PICT rsrc to a bitmap while writing the hack */
  120.  
  121.     PicHandle        pHdl;
  122.     WindowPtr        tWind;
  123.     Rect            tRect,wRect;
  124.     OSErr            iErr;
  125.     RgnHandle        mstRgn;
  126.     BitMap            bm, oldbm;
  127.     GrafPort        bmPort;
  128.  
  129.     if ( KM.option ) {
  130.         pHdl = (PicHandle)GetResource( 'PICT', 0 );
  131.         if ( pHdl )    {
  132.             tRect = (*pHdl)->picFrame;
  133.             OffsetRect(&tRect, 0 - tRect.left, 0 - tRect.top);
  134.             OpenPort(&bmPort);
  135.             bm.baseAddr = NewPtrClear(128000);
  136.             bm.bounds = tRect;
  137.             bm.bounds.right += 16;
  138.             bm.bounds.right &= ~15;
  139.             bm.rowBytes = bm.bounds.right / 8;
  140.             SetPortBits(&bm);
  141.             DrawPicture(pHdl,&tRect);
  142.             mstRgn = NewRgn();
  143.             iErr = BitMapToRegion(mstRgn,&bm);
  144.             AddResource((Handle)mstRgn,'mstp',0,"\pMST3000");
  145.             if ( !ResError() )    {
  146.                 WriteResource( (Handle)mstRgn );
  147.                 UpdateResFile( CurResFile() );
  148.             }
  149.             
  150.             ClosePort( &bmPort );
  151.             DisposeRgn( mstRgn );
  152.         }
  153.     }
  154. }    // DrawMST3()
  155.  
  156.  
  157. void Initialize( void )
  158. {
  159.     long                    total, contig;
  160.     long                     response;
  161.     EventRecord             event;
  162.     Handle                    menuBar;
  163.     short                    count;
  164.     OSErr                    iErr;
  165.     long                    result;
  166.     ProcessSerialNumber        aPSN;
  167.  
  168.     gInBackground = false;
  169.  
  170.     InitGraf((Ptr) &qd.thePort);
  171.     InitFonts();
  172.     InitWindows();
  173.     InitMenus();
  174.     InitCursor();
  175.  
  176.     for (count = 1; count <= 3; count++)
  177.         EventAvail(everyEvent, &event);
  178.  
  179.     PurgeSpace(&total, &contig);
  180.  
  181.     // got the speech mgr?
  182.     
  183.     count = Gestalt(gestaltSpeechAttr, &response);
  184.     if ( response )    {
  185.         menuBar = GetNewMBar(rMenuBar);            /* read menus into menu bar */
  186.         SetMenuBar(menuBar);                    /* install menus */
  187.         DisposHandle(menuBar);
  188.         AddResMenu(GetMHandle(mApple), 'DRVR');    /* add DA names to Apple menu */
  189.         DrawMenuBar();
  190.     }
  191.  
  192.     // this may come in handy someday
  193.     iErr = GetCurrentProcess( &gOurPSN );
  194.  
  195.     // politely send us to the background
  196.     aPSN.highLongOfPSN = 0;
  197.     aPSN.lowLongOfPSN = kNoProcess;
  198.     iErr = GetNextProcess( &aPSN );
  199.     while ( true ) {
  200.         if ( aPSN.lowLongOfPSN != gOurPSN.lowLongOfPSN )
  201.             break;
  202.         else if ( iErr = GetNextProcess( &aPSN ) )
  203.             break;
  204.     }
  205.     iErr = SetFrontProcess( &aPSN );
  206.  
  207. }    // Initialize()
  208.  
  209.  
  210. main()
  211. {
  212.     MaxApplZone();                /* expand the heap so code segments load at the top */
  213.  
  214.     Initialize();                    /* initialize the program */
  215.  
  216.     StartupSpeech();
  217.  
  218.     EventLoop();                    /* call the main event loop */
  219. }
  220.  
  221. SndChannel    sndZero;
  222. SndChannel    ChanSpace, *myChan;
  223.  
  224. void DoIdle( void )
  225. {
  226.     #define JORG_SELECTOR    'MSM3'
  227.     #define ARE_WE_INTERVAL    18000        // 5 minutes
  228.  
  229.     #define MSM_STR_ID        128
  230.     #define PREFS_IND        1
  231.     #define SUSPEND_IND        2
  232.  
  233.     // event-based messages - sent from the MSM 3K INIT
  234.     #define EB_STR_ID        129
  235.     #define    B_HELP            1    // User tried to turn on balloon help        ("Mommy, not the clown suit!")
  236.     #define    TRASH_WIMP        2    // User turned on trash warnings            ("What a wimp!")
  237.     #define    GATES_OF_HELL    3    // User ran a Microsoft product                ("Welcome to MS technical support.  Please hold for one hour")
  238.     #define CLEANUP            4    // User chose "Clean Up Window"                ("This will simplify everything")
  239.     #define ERASEDISK        5    // User chose "Erase Disk" in the Finder    ("Watch out, he's got a gun!")
  240.     #define    DONTTREADONME    6    // Clicking on the heads                    ("STOP TOUCHING ME")
  241.     #define    INSIDEADIALOG    7    // Inside a dialog too long                    ("Take your time, we're not going anywhere")
  242.     #define    MOVEMOUSELEFT    8    // To the right of a dialog too long        ("Left turn, Clyde")
  243.     #define    MOVEMOUSERIGHT    9    // TO the left of a dialog too long            ("Concentrate on the RIGHT")
  244.     #define    MOVEMOUSEDOWN    10    // On top of a dialog too long                ("Looks like you're right on top of things!")
  245.     #define    MOVEMOUSEUP        11    // Under a dialog too long                    ("Look up!  It's a plane!  It's a bird!  It's a dieolog box!")
  246.     #define    MAKINCOPIES        12    // Finder's makin' copies                    ("Take your time, we're not in aisle seats!")
  247.  
  248.     // time-based messages
  249.     #define TB_STR_ID        130
  250.     #define MAX_TB_STRINGS    3
  251.     #define FUN_YET            1
  252.     #define PUMA            2
  253.     #define DUMB_HACK        3
  254.  
  255.     OSErr            iErr;
  256.     pascal OSErr    (*MSM_Gestalt)(OSType selector, long *response);
  257.     long            speechCode;
  258.     Handle            snd;
  259.     SCStatus        scs;
  260.     short            err;
  261.  
  262.     if (myChan) {
  263.         if (SndChannelStatus(myChan, sizeof(scs), &scs) != noErr) return;
  264.         if (scs.scChannelBusy) return;
  265.         SndDisposeChannel(myChan, FALSE);
  266.         myChan = 0;
  267.     }
  268.     // we want to always check the selector as this clears it if non-nil
  269.     if ( (Gestalt( JORG_SELECTOR, (void *)&MSM_Gestalt ) == noErr) && !SpeechBusy() ) {
  270.         if (MSM_Gestalt('back', &speechCode) == noErr) {
  271.             if ( speechCode )    {
  272.                 if (snd = Get1Resource('snd ', 256 + speechCode)) {
  273.                     ChanSpace = sndZero;
  274.                     ChanSpace.qLength = stdQLength;
  275.                     myChan = &ChanSpace;
  276.                     err = SndNewChannel(&myChan, 0, initMono, 0);
  277.                     if (err == noErr) {
  278.                         err = SndPlay(myChan, snd, TRUE);
  279.                     } else myChan = 0;
  280.                 } else {
  281.                     GetIndString( gSpeechStr, EB_STR_ID, speechCode );
  282.                     SaySomething( (char *)gSpeechStr, false );
  283.                 }
  284.             }
  285.         }
  286.     } else if ( ((TickCount() - gLastSpeak) > ARE_WE_INTERVAL) && !SpeechBusy() )    {
  287.         gLastSpeak = TickCount();
  288.         GetIndString( gSpeechStr, TB_STR_ID, (gLastSpeak % MAX_TB_STRINGS) + 1 );
  289.         SaySomething( (char *)gSpeechStr, false );
  290.     }
  291. }    // DoIdle()
  292.  
  293.  
  294. void EventLoop( void )
  295. {
  296.     Boolean                gotEvent;
  297.     EventRecord        event;
  298.  
  299.     do {
  300.         gotEvent = WaitNextEvent(everyEvent, &event, 0, (RgnHandle)nil);
  301.  
  302.         if ( gotEvent ) {
  303.             DoEvent(&event);
  304.         } else {
  305.             DoIdle();
  306.         }
  307.     } while ( true );    /* loop forever; we quit via ExitToShell */
  308.  
  309. } /*EventLoop*/
  310.  
  311.  
  312. void DoEvent( EventRecord *event )
  313. {
  314.     short        part, err;
  315.     WindowPtr    window;
  316.     uchar key;
  317.     Point        aPoint;
  318.  
  319.     switch ( event->what ) {
  320.         case nullEvent:
  321.             /* we idle for null/mouse moved events ands for events which aren’t
  322.                 ours (see EventLoop) */
  323.             DoIdle();
  324.             break;
  325.         case mouseDown:
  326.             part = FindWindow(event->where, &window);
  327.             switch ( part ) {
  328.                 case inMenuBar:             /* process a mouse menu command (if any) */
  329.                     AdjustMenus();
  330.                     DoMenuCommand(MenuSelect(event->where));
  331.                     break;
  332.                 case inSysWindow:           /* let the system handle the mouseDown */
  333.                     SystemClick(event, window);
  334.                     break;
  335.             }
  336.             break;
  337.         case keyDown:
  338.         case autoKey:                       /* check for menukey equivalents */
  339.             key = (uchar) event->message & charCodeMask;
  340.             if ( event->modifiers & cmdKey ) {    /* Command key down */
  341.                 if ( event->what == keyDown ) {
  342.                     DoMenuCommand(MenuKey(key));
  343.                 }
  344.             }
  345.             break;
  346.         case activateEvt:
  347.             DoActivate((WindowPtr) event->message, (event->modifiers & activeFlag) != 0);
  348.             break;
  349.         case updateEvt:
  350.             DoUpdate((WindowPtr) event->message);
  351.             break;
  352.         case diskEvt:
  353.             if ( HiWord(event->message) != noErr ) {
  354.                 SetPt(&aPoint, 50, 50);
  355.                 err = DIBadMount(aPoint, event->message);
  356.             }
  357.             break;
  358.         case kOSEvent:
  359.         /*    1.02 - must BitAND with 0x0FF to get only low byte */
  360.             switch ((event->message >> 24) & 0x0FF) {        /* high byte of message */
  361.                 case kMouseMovedMessage:
  362.                     DoIdle();                    /* mouse-moved is also an idle event */
  363.                     break;
  364.                 case kSuspendResumeMessage:        /* suspend/resume is also an activate/deactivate */
  365.                     gInBackground = (event->message & kResumeMask) == 0;
  366.                     DoActivate(FrontWindow(), !gInBackground);
  367.                     break;
  368.             }
  369.             break;
  370.     }
  371. } /*DoEvent*/
  372.  
  373.  
  374. void DoActivate( WindowPtr fWind, Boolean becomingActive )
  375. {
  376.     if ( becomingActive ) SetCursor( &qd.arrow );
  377. }
  378.  
  379. void AdjustMenus( void )
  380. {
  381.     MenuHandle    mHdl;
  382.     short        i;
  383.  
  384.     CheckItem( GetMHandle( mFile ), iNoCmnt, gNoComment );
  385.  
  386.     mHdl = GetMHandle( mVoice );
  387.     for ( i=3 ; i<=gVoiceCount ; i++ )
  388.         CheckItem( mHdl, i, false );
  389.  
  390.     CheckItem( mHdl, gVoiceSel+2, true );
  391.  
  392. }
  393.  
  394. void DoMenuCommand( long menuResult )
  395. {
  396.     short        menuID, menuItem;
  397.     short        daRefNum;
  398.     Str255        daName;
  399.     WindowPtr    window;
  400.     Handle        aHandle;
  401.     OSErr        err;
  402.  
  403.     window = FrontWindow();
  404.     menuID = HiWord(menuResult);    /* use macros for efficiency to... */
  405.     menuItem = LoWord(menuResult);    /* get menu item number and menu number */
  406.     switch ( menuID ) {
  407.         case mApple:
  408.             switch ( menuItem ) {
  409.                 case iAbout:        /* bring up alert for About */
  410.                     DoAbout();
  411.                     break;
  412.                 default:            /* all non-About items in this menu are DAs et al */
  413.                     /* type Str255 is an array in MPW 3 */
  414.                     GetItem(GetMHandle(mApple), menuItem, daName);
  415.                     daRefNum = OpenDeskAcc(daName);
  416.                     break;
  417.             }
  418.             break;
  419.         case mFile:
  420.             switch ( menuItem ) {
  421.                 case iNoCmnt:
  422.                     DoNoComment();
  423.                     break;
  424.                 case iQuit:
  425.                     Terminate();
  426.                     break;
  427.             }
  428.             break;
  429.  
  430.         case mVoice:                            /* SPEECH MANAGER */
  431.             if (menuItem)
  432.                 {
  433.                 if (gSpeechChan)
  434.                     {
  435.                         err = DisposeSpeechChannel(gSpeechChan);
  436.                         gSpeechChan = NULL;
  437.                     }
  438.                 if ( menuItem == iPrefs )
  439.                     DoPrefs();
  440.                 else
  441.                     gVoiceSel = menuItem-2;
  442.                 }
  443.             break;
  444.     }
  445.     HiliteMenu(0);                    /* unhighlight what MenuSelect (or MenuKey) hilited */
  446. }
  447.  
  448. void DoUpdate( WindowPtr upWind )
  449. {
  450. }
  451.  
  452. void DoAbout( void )
  453. {
  454.     DialogPtr        theDLOG;
  455.     short            dItem = 0;
  456.  
  457.     if ( theDLOG = GetNewDialog( 128, nil, (WindowPtr)-1 ) )    {
  458.         ModalDialog( nil, &dItem );
  459.         DisposeDialog( theDLOG );
  460.     }
  461. }
  462.  
  463. void StartupSpeech( void )
  464. {
  465.     OSErr                err;
  466.     short                voiceCount;
  467.     short                i;
  468.     VoiceDescription    vd;
  469.     MenuHandle            voiceMenu;
  470.     NumVersion            ver = SpeechManagerVersion();
  471.  
  472.     #ifndef forRez
  473.     enum {development=0x20, alpha=0x40, beta=0x60, final=0x80, release=0x80};
  474.     #endif
  475.  
  476.     #define kMgrMajorVersion         1                /* 8-bits */
  477.     #define kMgrMinorVersion         0                /* 4-bits */
  478.     #define kMgrBugFixVersion         0                /* 4-bits */
  479.     #define kMgrDevelopmentStage     alpha            /* 8-bits */
  480.     #define kMgrNonRelVersion        2                /* 8-bits */
  481.     
  482.     #define kSpeechManagerVersion     ((kMgrMajorVersion        << 24)         \
  483.                                     | (kMgrMinorVersion     << 20)         \
  484.                                     | (kMgrBugFixVersion     << 16)         \
  485.                                     | (kMgrDevelopmentStage << 8)          \
  486.                                     | (kMgrNonRelVersion))
  487.                                     
  488.     voiceMenu = GetMHandle (mVoice);
  489.  
  490.     err = CountVoices(&voiceCount);
  491.     gVoiceCount = voiceCount;
  492.     if (voiceCount > kMaxVoices-1) voiceCount = kMaxVoices-1;
  493.     for (i = 1; i <= voiceCount; i++) {
  494.         err = GetIndVoice(i, &gVspec[i]);
  495.         
  496.         if ( true || ((*( ulong *) (&ver)) >= kSpeechManagerVersion)) {
  497.             // newer versions expect you to pass length in parameter list
  498.             err = GetVoiceDescription(&gVspec[i], &vd, sizeof(VoiceDescription));
  499.         } else {
  500.             //vd.length = sizeof(VoiceDescription);    // early versions require you to set length field
  501.             //err = GetVoiceDescription(&gVspec[i], &vd);
  502.         }
  503.         AppendMenu (voiceMenu, vd.name);
  504.     }
  505.     gVoiceSel = 0;                        // use Default voice
  506. }    // StartupSpeech()
  507.  
  508.  
  509. void SaySomething( char *sayThis, Boolean regardless )
  510. {
  511.     /*
  512.         note that the STR# resources use the first three chars to define
  513.         voice, speech rate, and speech pitch respectively. These are all based
  514.         on some rather crude scales that just get the job done.
  515.     */
  516.  
  517.     OSErr    iErr;
  518.  
  519.     if ( regardless || !gNoComment ) {
  520.  
  521.         if ( !gSpeechChan )    {
  522.             if (gVoiceSel == 0)
  523.                 iErr = NewSpeechChannel(0, &gSpeechChan);
  524.             else
  525.                 iErr = NewSpeechChannel(&gVspec[gVoiceSel], &gSpeechChan);
  526.         }
  527.  
  528.         if ( gSpeechChan ) {
  529.             MST3_ChangeVoiceSel( sayThis[1] - '@' );
  530.             MST3_ChangeRate( 0x00940000 + ((sayThis[2] - '0') * 0x00020000) );
  531.             MST3_ChangePitch( sayThis[3] - '0' );
  532.             iErr = SetSpeechInfo(gSpeechChan, soRate, &gSpeechRate);
  533.             iErr = SpeakText(gSpeechChan, (Ptr)&sayThis[4], sayThis[0]-3);
  534.         }
  535.     }
  536. }    // SaySomething()
  537.  
  538.  
  539. void DoPrefs( void )
  540. {
  541.     GetIndString( gSpeechStr, MSM_STR_ID, PREFS_IND );
  542.     SaySomething( (char *)gSpeechStr, true );
  543. }
  544.  
  545. void DoNoComment( void )
  546. {
  547.     gNoComment = !gNoComment;
  548.     if ( gNoComment )    {
  549.         GetIndString( gSpeechStr, MSM_STR_ID, SUSPEND_IND );
  550.         SaySomething( (char *)gSpeechStr, true );
  551.     }
  552. }
  553.  
  554. void Terminate( void )
  555. {
  556.     // dump any data???
  557.     while (myChan) DoIdle();
  558.     DrawMST3();
  559.     ExitToShell();
  560. }
  561.  
  562. void MST3_ChangeRate (Fixed newRate)
  563. {
  564.     Fixed        oldRate;
  565.     OSErr        err;
  566.     
  567.     if (gSpeechChan) {
  568.         err = GetSpeechRate(gSpeechChan, &oldRate);
  569.         err = SetSpeechRate(gSpeechChan, newRate);
  570.         gSpeechRate = newRate;                // save the new rate
  571.     }
  572. }
  573.  
  574. void MST3_ChangePitch (long newPitch)
  575. {
  576.     Fixed        thePitch;
  577.     OSErr        err;
  578.  
  579.     if (gSpeechChan) {
  580.         thePitch = Long2Fix(newPitch*10);
  581.         err = SetSpeechPitch( gSpeechChan, thePitch);
  582.     }
  583. }
  584.  
  585. void MST3_ChangeVoiceSel(long newVoice)
  586. {
  587.     if (newVoice <= gVoiceCount && newVoice > 0) gVoiceSel = newVoice;
  588.  
  589. }
  590.